import {IS_BROWSER,CANVAS} from './def'
import {randomStr,getBase64Url,style,getNumberfilterString} from './helper'
import GlobalEvent,{Cb as GlobalEventCb} from './GlobalEvent'

type InputElement = string|HTMLInputElement;

interface Options {
    element:string|HTMLElement,
    input:InputElement,
    width?:number,
    height?:number
}

function mergeDefaultOption(props:Options):Options{
    const SIZE = 500;

    return Object.assign({},props,{
        width:SIZE,
        height:SIZE
    })
}

function getInputElement(input:InputElement):HTMLInputElement{
    if(typeof input === 'string'){
        input
        const element = document.querySelector(input)
        if(!element){
            throw new Error(`${input}元素不存在`)   
        }
        if(!(element instanceof HTMLInputElement)){
            throw new Error(`${input}元素不是Input组件`)
        }
        input = element;
    }else if(!(input instanceof HTMLInputElement)){
        throw new Error('输入组件不是File类型Input')
    }

    if(input.setAttribute){
        input.setAttribute('type','file')
        input.setAttribute('accept','image/*')
    }else{
        input.type = "file"
        input.accept = "image/*"
    }

    return input;
}

export class Crop {

    /**
     * 裁剪图片的最外层容器
     */
    private container:HTMLElement;

    /**
     * 选择图片的Input控件
     */
    private inputElement:HTMLInputElement;

    private options:Options;

    private event:GlobalEvent;

    /**
     * 原图元素类名
     */
    private rawImageClassName:string = ".raw-image"

    /**
     * 唯一的容器类名
     */
    private containerClassName:string = this.randomClassName('container');

    constructor(options:Options){
        options = mergeDefaultOption(options)

        this.options = options

        // 初始化容器
        let {element} = options
        if(!IS_BROWSER) throw new Error('不是浏览器环境')
        if(!CANVAS) throw new Error('此浏览器不支持Canvas')
        if(typeof element==='string'){
            const ele = document.querySelector(element) as HTMLElement
            if(!ele) throw new Error('元素不存在')
            element = ele
        }

        while(!this.isUniqueSelector(this.containerClassName)){
            this.containerClassName = this.randomClassName('container');
        }
        
        // 给容器添加唯一的类名
        element.classList.add(this.containerClassName)
        this.container = element

        // 获取选择图片的Input控件
        this.inputElement = getInputElement(this.options.input)

        this.event = new GlobalEvent()
        
        // console.log(this.container,this.inputElement)

        this.listenFile()
        console.log(this.containerSizeNumber)

        return this;
    }

    listenFile(){
        this.inputElement.addEventListener('change',async ()=>{
            const {files} = this.inputElement
            const file = files && files.length?files[0]:null
            this.event.emit('changeFile',file)
            let url = ""
            if(file){
                url = await getBase64Url(file).catch(()=>"")
            }
            this.event.emit('dataUrlChange',url)
            this.drawRawImage(url)
        })
    }

    /**
     * 描绘原图
     */
    drawRawImage(url:string){
        const {container} = this

        const rawImage = new Image()

        const self = this

        // 重新设定宽度高度
        rawImage.onload = function(){
            const height = window.getComputedStyle(rawImage).height||""
            const width = window.getComputedStyle(rawImage).width||""

            const widthNumber = getNumberfilterString(width)
            const heightNumber = getNumberfilterString(height)

            const margin = [
                (self.containerSizeNumber.width - widthNumber) / 2,
                (self.containerSizeNumber.height - heightNumber) / 2
            ]

            console.log(margin,widthNumber,heightNumber)

            // 居中
            style(rawElement,{width,height,marginTop:margin[1]+"px",marginLeft:margin[0]+"px"},false)
            style(rawImage,{width,height})
        }
        rawImage.src = url
        style(rawImage,{
            maxWidth:this.containerSize.width,
            maxHeight:this.containerSize.height
        })

        

        let rawElement = container.querySelector(`img${this.rawImageClassName}`) as HTMLDivElement
        if(!rawElement){
            rawElement = document.createElement('div') as HTMLDivElement
            style(rawElement,{
                width:this.containerSize.width,
                height:this.containerSize.height
            })
            
            rawElement.appendChild(rawImage)
            container.appendChild(rawElement)
        }

    }

    /**
     * 监听事件
     * @param name 
     * @param cb 
     */
    add(name:string,cb:GlobalEventCb){
        this.event.add(name,cb)
        return this
    }

    /**
     * 移除事件监听
     * @param name 
     * @param cb 
     */
    remove(name:string,cb:GlobalEventCb){
        this.event.remove(name,cb)
        return this;
    }

    /**
     * 获取唯一的容器类名
     */
    getContainerClassName():string{
        return this.containerClassName
    }

    /**
     * 是否唯一的选择器
     * @param selector 
     */
    private isUniqueSelector(selector:string):boolean{
        return !document.querySelector(selector)
    }

    private randomClassName(name:string){
        return `${name}-${randomStr()}`
    }

    private get containerSize(){
        return {
            width:this.containerSizeNumber.width+"px",
            height:this.containerSizeNumber.height+"px"
        }
    }

    private get containerSizeNumber(){
        const style = window.getComputedStyle(this.container)
        const paddingHorizontal = getNumberfilterString(style.paddingLeft||"") + getNumberfilterString(style.paddingRight||"")
        const paddingVertical = getNumberfilterString(style.paddingTop||"") + getNumberfilterString(style.paddingBottom||"")
        return {
            width:this.container.scrollWidth - paddingHorizontal,
            height:this.container.scrollHeight - paddingVertical
        }
    }

}

export default Crop

if(IS_BROWSER){
    (window as typeof window & {Crop:typeof Crop}).Crop = Crop
}